package com.ak.game.service;

import com.ak.game.entity.PnGame;
import com.ak.game.entity.PnGameMineRecord;
import com.ak.game.entity.PnVipUser;
import com.ak.game.entity.UserMoneySession;
import com.ak.game.entity.vo.BetReqVo;
import com.ak.game.entity.vo.ClickReqVo;
import com.ak.game.entity.vo.ResultReqVo;
import com.ak.game.exception.ServiceException;
import com.ak.game.util.AjaxResult;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;

@Service
public class BettingService {

    @Autowired
    private UserMoneySessionService userMoneySessionService;

    @Autowired
    private PnVipUserService vipUserService;

    @Autowired
    private PnGameMineRecordService gameMineRecordService;

    @Autowired
    private PnGameService gameService;

    public static Map<String, Boolean> USER_IS_TRY_OUT = new HashMap<>();

    @Value("${game.id}")
    private String gameId;

    /**
     * 赔率
     */
    public static Map<Integer, BigDecimal[]> ODDS = new HashMap<>();

    /**
     * 杀率
     */
    private static Double winRate;

    /**
     * 试玩账号胜率
     */
    private static Integer tryOutRate;

    @PostConstruct
    private void initOdds() {
        ODDS.clear();
        PnGame pnGame = gameService.getPnGame();
        String config = pnGame.getConfig();
        JSONObject jsonObject = JSONObject.parseObject(config);
        tryOutRate = Integer.parseInt(jsonObject.remove("tryOutRate").toString());
        for (int i = 0; i < 24; i++) {
            List<BigDecimal> list = jsonObject.getList(String.valueOf(i + 1), BigDecimal.class);
            BigDecimal[] oddsArray = new BigDecimal[list.size()];
            for (int j = 0; j < list.size(); j++) {
                oddsArray[j] = list.get(j);
            }
            ODDS.put(i + 1, oddsArray);
        }
    }

    @PostConstruct
    private void initWinRate() {
        PnGame pnGame = gameService.getPnGame();
        winRate = pnGame.getWinRate();
    }

    private final Random random = new Random();

    public static Map<String, ChannelHandlerContext> USER_CTX_MAP = new HashMap<>();

    /**
     * 下注的方法
     *
     * @param betReqVo
     */
    @Transactional
    public void bet(BetReqVo betReqVo) {
        try {
            String userId = betReqVo.getData().get("user_id").toString();
            PnVipUser vipUser = vipUserService.getById(userId);
            // 玩家选择的地雷数量type 1-24
            Integer type = Integer.parseInt(betReqVo.getData().get("type").toString());
            String playSessionId = gameId + betReqVo.getGame().get("round_id") + vipUser.getBinding() + betReqVo.getData().get("type");
            // 插入user_money_session表，记录用户下注状态
            UserMoneySession userMoneySession = new UserMoneySession();
            userMoneySession.setPlaySessionId(playSessionId);
            userMoneySession.setBetMoney(new BigDecimal(betReqVo.getData().get("amount").toString()));
            userMoneySession.setMoneyNow(new BigDecimal(betReqVo.getData().get("amount").toString()));
            userMoneySession.setUserId(vipUser.getId());
            userMoneySession.setGameId(gameId);
            userMoneySession.setMultiple(BettingService.ODDS.get(Integer.parseInt(betReqVo.getData().get("type").toString()))[0]);
            userMoneySession.setPlayFrequency(0);
            userMoneySession.setType(type);
//            List<String> sysTypeList = new ArrayList<>();
//            do {
//                int type = random.nextInt(25) + 1;
//                if (!sysTypeList.contains(String.valueOf(type))) {
//                    sysTypeList.add(String.valueOf(type));
//                }
//            } while (sysTypeList.size() < userMoneySession.getType());
//            String sysType = String.join(",", sysTypeList);
//            userMoneySession.setSysSelect(sysType);
            userMoneySessionService.save(userMoneySession);
        } catch (Exception e) {
            if (e.getCause() instanceof SQLIntegrityConstraintViolationException) {
                throw new ServiceException("10009");
            }
        }
    }

    /**
     * 扫雷
     *
     * @param clickReqVo
     */
    @Transactional
    public PnGameMineRecord click(ClickReqVo clickReqVo, ChannelHandlerContext ctx) {
        // 查询UserMoneySession表数据
        LambdaQueryWrapper<UserMoneySession> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserMoneySession::getUserId, clickReqVo.getUserId());
        lambdaQueryWrapper.eq(UserMoneySession::getGameId, gameId);
        UserMoneySession userMoneySession = userMoneySessionService.getOne(lambdaQueryWrapper);
        // 查询gameMineRecord表数据
        LambdaQueryWrapper<PnGameMineRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(PnGameMineRecord::getPlaysessionId, userMoneySession.getPlaySessionId());
        queryWrapper.eq(PnGameMineRecord::getIsMine, "1");
        queryWrapper.eq(PnGameMineRecord::getGameId, gameId);
        List<PnGameMineRecord> pnGameMineRecords = gameMineRecordService.getBaseMapper().selectList(queryWrapper);
        List<String> userSelects = pnGameMineRecords.stream().map(PnGameMineRecord::getUserSelect).collect(Collectors.toList());
        // 判断用户点击的格子是不是地雷
        Integer type = clickReqVo.getType();
        PnGameMineRecord gameMineRecord = new PnGameMineRecord();
        gameMineRecord.setGameId(gameId);
        gameMineRecord.setUserId(clickReqVo.getUserId());
        gameMineRecord.setRecordId(userMoneySession.getPlaySessionId());
        gameMineRecord.setPlaysessionId(userMoneySession.getPlaySessionId());
        gameMineRecord.setStartTime(new Date());
        gameMineRecord.setEndTime(new Date());
        gameMineRecord.setAmount(userMoneySession.getBetMoney());
        gameMineRecord.setItemStr(JSON.toJSONString(clickReqVo));
        gameMineRecord.setUserSelect(String.valueOf(type));
        gameMineRecord.setIsCash("1");
//        boolean isCash = false;
        // 试玩账号
        if (USER_IS_TRY_OUT.get(clickReqVo.getUserId())) {
            int ran = random.nextInt(100);
            // 试玩账号没点到雷
            if (ran < tryOutRate && pnGameMineRecords.size() < 25 - userMoneySession.getType()) {
                gameMineRecord.setMultiple(ODDS.get(userMoneySession.getType())[userMoneySession.getPlayFrequency()]);
                gameMineRecord.setWin(userMoneySession.getBetMoney().multiply(ODDS.get(userMoneySession.getType())[userMoneySession.getPlayFrequency()]));
                gameMineRecord.setIsMine("1");
                gameMineRecordService.save(gameMineRecord);
                userMoneySession.setPlayFrequency(userMoneySession.getPlayFrequency() + 1);
                userMoneySession.setMoneyNow(gameMineRecord.getWin());
                userMoneySession.setMultiple(gameMineRecord.getMultiple());
                userMoneySessionService.updateById(userMoneySession);
                // 没点到雷，判断一下是否没有雷了，自动结算
                if (pnGameMineRecords.size() + 1 == 25 - userMoneySession.getType()) {
                    this.autoCash(clickReqVo.getUserId(), ctx);
                    return null;
                }
            } else { // 试玩账号点到雷了
                gameMineRecord.setMultiple(BigDecimal.ZERO);
                gameMineRecord.setWin(BigDecimal.ZERO);
                gameMineRecord.setIsMine("0");
                userMoneySessionService.remove(lambdaQueryWrapper);
                List<String> sysSelects = new ArrayList<>();
                sysSelects.add(String.valueOf(type));
                // 生成地雷格
                for (int i = 0; i < userMoneySession.getType() - 1; i++) {
                    while (true) {
                        String mine = String.valueOf(this.random.nextInt(25) + 1);
                        if (!userSelects.contains(mine) && !mine.equals(type) && !sysSelects.contains(mine)) {
                            sysSelects.add(mine);
                            break;
                        }
                    }
                }
                // 地雷
                String sysSelect = String.join(",", sysSelects);
                gameMineRecord.setSysSelect(sysSelect);
                LambdaUpdateWrapper<PnGameMineRecord> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.eq(PnGameMineRecord::getPlaysessionId, userMoneySession.getPlaySessionId());
                updateWrapper.set(PnGameMineRecord::getSysSelect, sysSelect);
                gameMineRecordService.save(gameMineRecord);
            }
            return gameMineRecord;
        } else {
            // 得到的结果为杀率winRate参与计算后玩家的胜率，除数一直都为25，则游戏越进行到后面，胜率越低
            double rate = ((double) (25 - pnGameMineRecords.size() - userMoneySession.getType()) / 25) * winRate;
            double random = this.random.nextDouble();
            // 系统生成的地雷格子编号
//        String sysSelect = userMoneySession.getSysSelect();
//        List<String> sysTypeList = Arrays.stream(sysSelect.split(",")).collect(Collectors.toList());
            // random小于rate则玩家此次没有点击到地雷，反之则玩家点击到了地雷。
            if (random < rate) {
                gameMineRecord.setMultiple(ODDS.get(userMoneySession.getType())[userMoneySession.getPlayFrequency()]);
                gameMineRecord.setWin(userMoneySession.getBetMoney().multiply(ODDS.get(userMoneySession.getType())[userMoneySession.getPlayFrequency()]));
                gameMineRecord.setIsMine("1");
                gameMineRecordService.save(gameMineRecord);
                userMoneySession.setPlayFrequency(userMoneySession.getPlayFrequency() + 1);
                userMoneySession.setMoneyNow(gameMineRecord.getWin());
                userMoneySession.setMultiple(gameMineRecord.getMultiple());
                userMoneySessionService.updateById(userMoneySession);
                // 没点到雷，判断一下是否没有雷了，自动结算
                if (pnGameMineRecords.size() + 1 == 25 - userMoneySession.getType()) {
                    this.autoCash(clickReqVo.getUserId(), ctx);
                    return null;
                }
            } else { // 点到雷了
                gameMineRecord.setMultiple(BigDecimal.ZERO);
                gameMineRecord.setWin(BigDecimal.ZERO);
                gameMineRecord.setIsMine("0");
                userMoneySessionService.remove(lambdaQueryWrapper);
                List<String> sysSelects = new ArrayList<>();
                sysSelects.add(String.valueOf(type));
                // 生成地雷格
                for (int i = 0; i < userMoneySession.getType() - 1; i++) {
                    while (true) {
                        String mine = String.valueOf(this.random.nextInt(25) + 1);
                        if (!userSelects.contains(mine) && !mine.equals(type) && !sysSelects.contains(mine)) {
                            sysSelects.add(mine);
                            break;
                        }
                    }
                }
                // 地雷
                String sysSelect = String.join(",", sysSelects);
                gameMineRecord.setSysSelect(sysSelect);
                LambdaUpdateWrapper<PnGameMineRecord> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.eq(PnGameMineRecord::getPlaysessionId, userMoneySession.getPlaySessionId());
                updateWrapper.set(PnGameMineRecord::getSysSelect, sysSelect);
                gameMineRecordService.save(gameMineRecord);
            }
            return gameMineRecord;
        }
    }


    @Transactional
    public void autoCash(String userId, ChannelHandlerContext ctx) {
        PnGameMineRecord gameMineRecord1003 = cash(userId);
        // 返回给前端
        ctx.channel().writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(AjaxResult.success(gameMineRecord1003, "1003"))));
        // 发送给接口api
        try {
            ResultReqVo resultReqVo = new ResultReqVo();
            resultReqVo.setMsg_id("109");
            Map<String, Object> data1003 = new HashMap<>();
            data1003.put("user_id", userId);
            data1003.put("type", gameMineRecord1003.getSysSelect().split(",").length);
            data1003.put("amount", gameMineRecord1003.getAmount());
            data1003.put("win", gameMineRecord1003.getWin());
            data1003.put("multiple", gameMineRecord1003.getMultiple());
            resultReqVo.setData(data1003);
            Map<String, Object> game1003 = new HashMap<>();
            game1003.put("round_id", gameMineRecord1003.getPlaysessionId());
            game1003.put("id", gameId);
            game1003.put("start_time", gameMineRecord1003.getStartTime());
            game1003.put("end_time", gameMineRecord1003.getEndTime());
            resultReqVo.setGame(game1003);
            CompletableFuture<Object> objectCompletableFuture1 = RemoteWebSocketClientService.sendMessage(JSON.toJSONString(resultReqVo));
            Boolean response = true;
            do {
                response = (Boolean) objectCompletableFuture1.get(10, TimeUnit.SECONDS);
            } while (!response);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            throw new RuntimeException(e);
        }
    }

    @Transactional
    public PnGameMineRecord cash(String userId) {
        // 查询UserMoneySession表的数据
        LambdaQueryWrapper<UserMoneySession> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserMoneySession::getUserId, userId);
        lambdaQueryWrapper.eq(UserMoneySession::getGameId, gameId);
        UserMoneySession userMoneySession = userMoneySessionService.getOne(lambdaQueryWrapper);
        // 查询PnGameMineRecord表的数据
        LambdaQueryWrapper<PnGameMineRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(PnGameMineRecord::getPlaysessionId, userMoneySession.getPlaySessionId());
        List<PnGameMineRecord> pnGameMineRecords = gameMineRecordService.getBaseMapper().selectList(queryWrapper);
        List<String> userSelects = pnGameMineRecords.stream().map(PnGameMineRecord::getUserSelect).collect(Collectors.toList());
        // 生成地雷格
        List<String> sysSelects = new ArrayList<>();
        for (int i = 0; i < userMoneySession.getType(); i++) {
            while (true) {
                String mine = String.valueOf(this.random.nextInt(25) + 1);
                if (!userSelects.contains(mine) && !sysSelects.contains(mine)) {
                    sysSelects.add(mine);
                    break;
                }
            }
        }
        // 地雷
        String sysSelect = String.join(",", sysSelects);
        LambdaUpdateWrapper<PnGameMineRecord> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(PnGameMineRecord::getPlaysessionId, userMoneySession.getPlaySessionId());
        updateWrapper.set(PnGameMineRecord::getSysSelect, sysSelect);

        PnGameMineRecord gameMineRecord = new PnGameMineRecord();
        gameMineRecord.setGameId(gameId);
        gameMineRecord.setUserId(userId);
        gameMineRecord.setRecordId(userMoneySession.getPlaySessionId());
        gameMineRecord.setPlaysessionId(userMoneySession.getPlaySessionId());
        gameMineRecord.setStartTime(new Date());
        gameMineRecord.setEndTime(new Date());
        gameMineRecord.setAmount(userMoneySession.getBetMoney());
        gameMineRecord.setItemStr(JSON.toJSONString(Map.of("user_id", userId, "msg_id", "1003")));
        gameMineRecord.setUserSelect("");
        gameMineRecord.setSysSelect(sysSelect);
        gameMineRecord.setIsCash("0");
        gameMineRecord.setMultiple(userMoneySession.getMultiple());
        gameMineRecord.setWin(userMoneySession.getMoneyNow());
        gameMineRecord.setIsMine("1");
        gameMineRecordService.save(gameMineRecord);
        // 删除user_money_session表的数据
        userMoneySessionService.remove(lambdaQueryWrapper);
        return gameMineRecord;
    }

    /**
     * 刷新游戏配置
     */
    public void flushConfig() {
        // 获取新的游戏配置
        initOdds();
        // 返回到1分钟游戏场
        for (Map.Entry<String, ChannelHandlerContext> item : BettingService.USER_CTX_MAP.entrySet()) {
            item.getValue().channel().writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(AjaxResult.success(ODDS, "1004"))));
        }
    }

    public void flushWinRate() {
        initWinRate();
    }
}
